package etcdv2

import (
	"context"
	"errors"
	//"flag"
	"fmt"
	"go.etcd.io/etcd/client/v3"
	"go.etcd.io/etcd/client/v3/concurrency"
	"log"
	"time"
)

type Config struct {
	EleName   string
	NodeName  string
	Endpoints []string
}

type NodeServer struct {
	//electName EleName 表示竞选的哪儿
	//nodeName NodeName 表示被竞选时的NodeName(节点名)
	cli       *clientv3.Client
	leaseId   clientv3.LeaseID
	aliveChan <-chan *clientv3.LeaseKeepAliveResponse
	session   *concurrency.Session
	el        *concurrency.Election
	c         *Config
}

// NewNodeServer 默认自建一个ttl为5s的选举制NodeServer
func NewNodeServer(config *Config) (s *NodeServer, err error) {
	fmt.Println(config)
	//var conf Config
	client, err := clientv3.New(clientv3.Config{
		Endpoints:   config.Endpoints,
		DialTimeout: 5 * time.Second,
		//DialKeepAliveTime: 5 * time.Second,
	})
	s = &NodeServer{}
	s.c = config
	fmt.Println(s.c.EleName)
	if err != nil {
		return &NodeServer{}, err
	}
	//s = &NodeServer{cli: client}
	s.cli = client
	//err = s.NewLease(5)
	if err != nil {
		return &NodeServer{}, err
	}
	if err = s.Leader(); err != nil {
		log.Println("s.Leader() err", err)
		return
	}
	return s, nil
}

// NewLease 为客户端创建生成leaseId和aliveChan，在选举时不用这个
func (s *NodeServer) NewLease(ttl int64) (err error) {
	lease := clientv3.NewLease(s.cli)
	l, err := lease.Grant(context.TODO(), ttl)
	if err != nil {
		return
	}
	s.leaseId = l.ID
	aliveChan, err := lease.KeepAlive(context.TODO(), s.leaseId)
	if err != nil {
		return
	}
	s.aliveChan = aliveChan
	return nil
}

// Leader 是选举中心，用于整合调用选举相关的方法，
// 若此节点不是leader将持续循环监控，在监控到没有leader时将进行选举，
// 选举失败将继续监控
func (s *NodeServer) Leader() (err error) {
	// NewSession gets the leased session for a client.
	//在代码中，我们可以发现其调用了client.Grant(ops.ctx, int64(ops.ttl))
	//和起了一个keepAlive的协程，因此调用了此方法就不需要自己去申请一个租期
	session, err := concurrency.NewSession(s.cli, concurrency.WithTTL(5))
	if err != nil {
		return
	}
	//开始一直找不到选举的对象，是因为我爸这个事件session给关了
	//defer session.Close()
	//s.EleName表示竞选的哪儿
	//fmt.Println("___________", s.c.EleName)
	election := concurrency.NewElection(session, s.c.EleName)
	s.el = election
	err = s.beLeader()
	if err != nil {
		fmt.Println(err)
	}
	return nil
}

// beLeader 应该在发现没有leader时候调用,表示开始竞争leader
// 未成为leader就一直阻塞，成为了leader才return
func (s *NodeServer) beLeader() (err error) {
	//ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	//defer cancel()
	//s.NodeName 表示被竞选时的NodeName(节点名),没有cancel就一直阻塞
	err = s.el.Campaign(context.TODO(), s.c.NodeName)
	log.Println(s.c.NodeName + "参与竞选")
	if err != nil {
		log.Println("竞选失败", err)
		return err
	}
	log.Println("竞选成功")
	return
}

// Monitor 返回当前EleName是否有leader。
func (s *NodeServer) Monitor() (ifLeader bool) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	res, err := s.el.Leader(ctx)
	//没有leader
	if err == concurrency.ErrElectionNoLeader {
		log.Println("没有leader")
		return false //有leader，后续得持续循环监控
	}
	//debug
	log.Println(res.Kvs)
	return true
}

// StartServer 将检测此节点是否为leader，若是，则开始服务的正常运行，若不是，则返回错误
func (s *NodeServer) StartServer() (err error) {
	time.Sleep(2 * time.Second)
	//返回当前的leader是谁
	res, err := s.el.Leader(context.TODO())
	if err != nil {
		log.Println(err)
	}
	//	fmt.Println(res.Kvs[0].NodeNameue)
	//fmt.Println(s.c.NodeName)
	if string(res.Kvs[0].Value) == s.c.NodeName {
		//fmt.Println(res.Kvs[0].NodeNameue)
		fmt.Println(s.c.NodeName)
		return nil
	}
	return errors.New("此节点不是leader")
}
